#include "MelodyEvaluator.h"
#include "QString"
#include "math.h"
#include "QDebug"
#include "RandomNumberGenerator.h"


//Extremal Optimization of individual parameters.
//Transpose elements  on h and v axis to introduce diversity and improve fitness of solutions.
MelodyEvaluator::MelodyEvaluator(float targetValue,int generationLimit,const float tonality[12][12])
    : FitnessEvaluator(targetValue,generationLimit) {
    //this->tonality = tonality;
    for(int x=0;x<12;x++) {
        for(int y=0;y<12;y++) {
            this->tonality[x][y] = tonality[x][y];
        }
    }
}

float MelodyEvaluator::getFitness(Candidate* candidate) {
    float rhythmFitness = 0;
    //float averageRVal = 0;
    float melodyFitness = 0;
    int noteFitness = 0;
    //float averageMVal = 0;
    int previousR = 0;
    int twoPrevM = 0;
    int previousM = 0;

    int previousFitness = 0;
    int weakestNote = 0;
    float totalLength = 0;
    int note = 0;
    //Calculate averages
    for(int i=0;i<candidate->getGenome().size();) {

        //RHYTHM
        if(candidate->getChromosome(i)!= previousR) {
            rhythmFitness -= 0.025;
            noteFitness -= 0.025;
        }
        if(
                (i==0) &&
                ((candidate->getChromosome(i)!=5)||
                 (candidate->getChromosome(i)!=7))
        ) {
            rhythmFitness += 15;
        }
        //averageRVal += candidate->getGenome().at(i);
        previousR = candidate->getGenome().at(i);
        switch((int)candidate->getChromosome(i)) {
            case 0:
                totalLength += 0.25;
                rhythmFitness += 0.25;
                noteFitness += 0.25;
                break;
            case 1:
                totalLength += 0.25;
                rhythmFitness += 0.25;
                noteFitness += 0.25;
                break;
            case 2:
                totalLength += 0.25;
                rhythmFitness += 0.25;
                noteFitness += 0.25;
                break;
            case 3:
                totalLength += 0.5;
                rhythmFitness += 0.25;
                noteFitness += 0.25;
                break;
            case 4:
                totalLength += 0.5;
                rhythmFitness += 0.25;
                noteFitness += 0.25;
                break;
            case 5:
                totalLength += 1;
                rhythmFitness += 0.333;
                noteFitness += 0.333;
                break;
            case 6:
                totalLength += 0.75;
                rhythmFitness += 0.5;
                noteFitness += 0.5;
                break;
            case 7:
                totalLength += 1.5;
                rhythmFitness += 0.333;
                noteFitness += 0.333;
                break;
        }
        i++;

        //MELODY
        twoPrevM = candidate->getChromosome((i-12)%candidate->getGenome().size());
        previousM = candidate->getChromosome((i-6)%candidate->getGenome().size());
        melodyFitness += this->tonality[previousM%12][(int)candidate->getChromosome(i)%12] *1.15;
        noteFitness += this->tonality[previousM%12][(int)candidate->getChromosome(i)%12] *1.15;
        if(candidate->getChromosome(i) > (previousM +12)) {
            melodyFitness += 0.25;
            noteFitness += 0.25;
        }
        if(candidate->getChromosome(i) < (previousM -12)) {
            melodyFitness += 0.25;
            noteFitness += 0.25;
        }
        if(candidate->getChromosome(i) < 40) {
            melodyFitness += 1;
            noteFitness += 1;
        }
        if(candidate->getChromosome(i) > 80) {
            melodyFitness += 1;
            noteFitness += 1;
        }
        if(candidate->getChromosome(i) == previousM) {
            melodyFitness += 1.5;
            noteFitness += 1.5;
        }
        if(candidate->getChromosome(i) == twoPrevM) {
            melodyFitness += 1.5;
            noteFitness += 1.5;
        }
        //averageMVal += candidate->getGenome().at(i);
        //previousM = candidate->getChromosome(i);
        i++;

        i++;
        i++;
        //if(candidate->getChromosome(i)!= previousR) {
        //    arg1Fitness += 0.01;
        //}
        i++;
        i++;
        if(noteFitness > previousFitness) {
            weakestNote = note;
            previousFitness = noteFitness;
        }
        note++;
    }
    if(totalLength <8) {
        rhythmFitness += 50;
    } else if(totalLength > 16){
        rhythmFitness += 50;
    }
    //averageRVal = averageRVal/(candidate->getGenome().size()/6);
    //averageMVal = averageMVal/(candidate->getGenome().size()/6);
    //Calculate consistency
    //for(int i=0;i<candidate->getGenome().size();) {
        //rhythmFitness += abs(averageRVal - candidate->getGenome().at(i)) *0.0125;
    //    i++;
        //melodyFitness += abs(averageMVal - candidate->getGenome().at(i)) *0.001;
    //    i++;
    //    i++;
    //    i++;
    //    i++;
    //    i++;
    //}
    //candidate->setFinalValue(rhythmFitness + melodyFitness);
    fitness = rhythmFitness + melodyFitness;
    //qDebug() << fitness;
    //candidate->extremalOptimization(weakestNote);
    candidate->setFitness(fitness);
    return fitness;
}

Candidate* MelodyEvaluator::finalize(Candidate* candidate) {

    //QString printString;
    //QString numString;


    qDebug() << "Generation: " << generation;
    //qDebug() << "FinalVal: " << finalVal;
    qDebug() << "Fitness: " << candidate->getFitness();
    candidate->printGenome();
    //qDebug() << printString;
    //this->finalValue = finalVal;
    //finalNum = winner.data()->
    return candidate;
}

void MelodyEvaluator::setTonality(const float tonality[12][12]) {
    for(int x=0;x<12;x++) {
        for(int y=0;y<12;y++) {
            this->tonality[x][y] = tonality[x][y];
        }
    }
}
